using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace UnmanagedClass
{
    [DebuggerTypeProxy(typeof(NativeSimpleArrayDebugView<>))]
    public unsafe struct NativeSimpleArray<T> : IDisposable
        where T : unmanaged
    {
        internal void* m_Buffer;
        internal int m_Length;

        public int Length
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get => m_Length;
        }

        public T* Ptr
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get => (T*)m_Buffer;
        }

        /// <summary>不安全：直接设置 buffer 指针</summary>
        public void UnsafeSetBufferPtr(ulong addr)
        {
            m_Buffer = (void*)addr;
        }

        public bool IsCreated
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get => m_Buffer != null;
        }

        public void Dispose()
        {
            m_Buffer = null;
            m_Length = 0;
        }

        public T this[int index]
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                CheckIndex(index);
                return *((T*)m_Buffer + index);
            }
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            set
            {
                CheckIndex(index);
                *((T*)m_Buffer + index) = value;
            }
        }

        public ref T GetRefAt(int index)
        {
            CheckIndex(index);
            return ref *((T*)m_Buffer + index);
        }

        public T* GetPtrAt(int index)
        {
            CheckIndex(index);
            return (T*)m_Buffer + index;
        }

        public void* GetUnsafePtr() => m_Buffer;

        public void CopyFrom(T[] src)
        {
            if (src == null) throw new ArgumentNullException(nameof(src));
            if (src.Length > m_Length) throw new ArgumentException("Source array is larger than target buffer");

            fixed (T* pSrc = src)
            {
                Buffer.MemoryCopy(pSrc, m_Buffer, m_Length * sizeof(T), src.Length * sizeof(T));
            }
        }

        [Conditional("DEBUG")]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void CheckIndex(int index)
        {
            if (index >= m_Length || index < 0)
                throw new IndexOutOfRangeException();
        }
    }

    internal class NativeSimpleArrayDebugView<T> where T : unmanaged
    {
        private NativeSimpleArray<T> m_Array;

        public NativeSimpleArrayDebugView(NativeSimpleArray<T> array)
        {
            m_Array = array;
        }

        public unsafe T[] Items
        {
            get
            {
                if (!m_Array.IsCreated) return null;

                int length = m_Array.m_Length;
                var array = new T[length];
                fixed (T* pDst = array)
                {
                    Buffer.MemoryCopy(m_Array.m_Buffer, pDst, length * sizeof(T), length * sizeof(T));
                }
                return array;
            }
        }
    }
}
